﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Browser;
using Moq;
using NUnit.Framework;
using HtmlUtilities.UITest;
using NUnit.Framework.SyntaxHelpers;

namespace HtmlUtilities.UnitTests
{
    [TestFixture]
    public class MainPageViewModelTests
    {
        private MockHtml _mockHtml;
        private MainPageViewModel _viewModel;

        private Mock<IHtmlElement> _mockTestDiv;

        [SetUp]
        public void SetUp()
        {
            _mockHtml = new MockHtml();
            _viewModel = new MainPageViewModel();

            _mockTestDiv = new Mock<IHtmlElement>();
            _mockTestDiv.Setup(div => div.TagName).Returns("div");
            _mockTestDiv.Setup(div => div.Id).Returns("testing");
            _mockHtml.Document.Setup(doc => doc.GetElementById("testing")).Returns(_mockTestDiv.Object);
        }

        [TearDown]
        public void TearDown()
        {
            _mockHtml.TearDown();
        }

        private static Command<string> GetCommand(IEnumerable<Command<string>> commands, string commandName)
        {
            return (from command in commands
                    where command.CommandName == commandName
                    select command).FirstOrDefault();
        }

        private Command<string> BrowserCommand(string commandName)
        {
            return GetCommand(_viewModel.BrowserCommands, commandName);
        }

        private Command<string> DocumentCommand(string commandName)
        {
            return GetCommand(_viewModel.DocumentCommands, commandName);
        }

        private Command<string> WindowCommand(string commandName)
        {
            return GetCommand(_viewModel.WindowCommands, commandName);
        }

        private Command<string> ElementCommand(string commandName)
        {
            return GetCommand(_viewModel.ElementCommands, commandName);
        }

        private Command<string> HtmlObjectCommand(string commandName)
        {
            return GetCommand(_viewModel.HtmlObjectCommands, commandName);
        }

        private Command<string> ScriptObjectCommand(string commandName)
        {
            return GetCommand(_viewModel.ScriptObjectCommands, commandName);
        }

        private Command<string> PageCommand(string commandName)
        {
            return GetCommand(_viewModel.PageCommands, commandName);
        }

        private Command<string> SOCOllectionCommand(string commandName)
        {
            return GetCommand(_viewModel.ScriptObjectCollectionCommands, commandName);
        }

        [Test]
        public void UnknownCommand_Sets_Output_To_Error()
        {
            _viewModel.ExecuteCommand(BrowserCommand("NotThere"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("Command Not Found"));
        }

        [Test]
        public void Setting_ViewModel_Property_Fires_PropertyChanged()
        {
            string propertyChanged = string.Empty;
            _viewModel.PropertyChanged += (s, arg) => propertyChanged = arg.PropertyName;

            _viewModel.ExecuteCommand(BrowserCommand("Anything"));

            Assert.That(propertyChanged, Is.EqualTo("CommandResult"));
        }

        [Test]
        public void BrowserVersion_Displays_Browser_Version()
        {
            _mockHtml.BrowserInformation.SetupGet(info => info.BrowserVersion).Returns(new Version(1, 2));

            _viewModel.ExecuteCommand(BrowserCommand("BrowserVersion"));
            
            Assert.That(_viewModel.CommandResult, Is.EqualTo("1.2"));
        }

        [Test]
        public void CookiesEnabled_Displays_Cookies_Status()
        {
            _mockHtml.BrowserInformation.SetupGet(info => info.CookiesEnabled).Returns(true);
            
            _viewModel.ExecuteCommand(BrowserCommand("CookiesEnabled"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("True"));
        }

        [Test]
        public void Name_Displays_Browser_Name()
        {
            _mockHtml.BrowserInformation.SetupGet(info => info.Name).Returns("MyBrowser");

            _viewModel.ExecuteCommand(BrowserCommand("Name"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("MyBrowser"));
        }

        [Test]
        public void Platform_Displays_OS_Platform()
        {
            _mockHtml.BrowserInformation.SetupGet(info => info.Platform).Returns("Windoze");

            _viewModel.ExecuteCommand(BrowserCommand("Platform"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("Windoze"));
        }

        [Test]
        public void UserAgent_Displays_Browser_Agent()
        {
            _mockHtml.BrowserInformation.SetupGet(info => info.UserAgent).Returns("a: b: c");

            _viewModel.ExecuteCommand(BrowserCommand("UserAgent"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("a: b: c"));
        }

        [Test]
        public void DocumentBody_Displays_TagName_Of_Body_Element()
        {
            var mockBody = new Mock<IHtmlElement>();
            mockBody.SetupGet(body => body.TagName).Returns("DIV");
            _mockHtml.Document.SetupGet(doc => doc.Body).Returns(mockBody.Object);

            _viewModel.ExecuteCommand(DocumentCommand("Body"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("DIV"));
        }

        [Test]
        public void DocumentCookies_Displays_Cookies()
        {
            _mockHtml.Document.SetupGet(doc => doc.Cookies).Returns("TastyOatmeal");

            _viewModel.ExecuteCommand(DocumentCommand("Cookies"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("TastyOatmeal"));
        }

        [Test]
        public void DocumentElement_Displays_TagName_Of_Root_Element()
        {
            var mockBody = new Mock<IHtmlElement>();
            mockBody.SetupGet(body => body.TagName).Returns("HTML");
            _mockHtml.Document.SetupGet(doc => doc.DocumentElement).Returns(mockBody.Object);

            _viewModel.ExecuteCommand(DocumentCommand("DocumentElement"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("HTML"));
        }

        [Test]
        public void DocumentUri_Displays_Location()
        {
            var uri = new Uri("http://www.cnn.com");
            _mockHtml.Document.SetupGet(doc => doc.DocumentUri).Returns(uri);

            _viewModel.ExecuteCommand(DocumentCommand("DocumentUri"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("http://www.cnn.com/"));
        }

        [Test]
        public void DocumentIsReady_Displays_If_Document_Is_Ready()
        {
            _mockHtml.Document.SetupGet(doc => doc.IsReady).Returns(false);

            _viewModel.ExecuteCommand(DocumentCommand("IsReady"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("False"));
        }

        [Test]
        public void DocumentQueryString_Displays_The_Query()
        {
            var query = new Dictionary<string, string> { {"a", "b"}, {"c", "d"} };
            _mockHtml.Document.SetupGet(doc => doc.QueryString).Returns(query);

            _viewModel.ExecuteCommand(DocumentCommand("QueryString"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("a=b&c=d"));
        }

        private void ExpectCreateElement(string tag)
        {
            var mockElement = new Mock<IHtmlElement>();
            _mockHtml.Document.Setup(doc => doc.CreateElement(tag)).Returns(mockElement.Object).Verifiable();
        }

        [Test]
        public void CreateElementTest_Creates_Element_And_Appends_It()
        {
            ExpectCreateElement("a");

            _viewModel.ExecuteCommand(DocumentCommand("CreateElement"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("Expect a CNN link"));
            _mockHtml.Document.Verify();
        }
        
        private static Mock<IHtmlElement> GetElement(string id)
        {
            var mockElement = new Mock<IHtmlElement>();
            mockElement.SetupGet(element => element.Id).Returns(id);
            return mockElement;
        }

        private static Mock<IScriptObjectCollection> GetElements(params string[] ids)
        {
            var mockElements = from id in ids
                               let mock = GetElement(id)
                               select mock.Object as IScriptObject;

            var mockCollection = new Mock<IScriptObjectCollection>();
            mockCollection.Setup(collection => collection.GetEnumerator()).Returns(mockElements.GetEnumerator());
            return mockCollection;
        }

        [Test]
        public void GetElementsByTagName_Finds_All_DIVs()
        {
            _mockHtml.Document.Setup(doc => doc.GetElementsByTagName("div")).Returns(GetElements("Foo", "Bar", "Baz").Object);

            _viewModel.ExecuteCommand(DocumentCommand("GetElementsByTagName"));

            Assert.That(_viewModel.CommandResult.Contains("Foo, Bar, Baz"));
        }

        [Test]
        public void Submit_Submits_The_Document()
        {
            _mockHtml.Document.Setup(doc => doc.Submit()).Verifiable();

            _viewModel.ExecuteCommand(DocumentCommand("Submit"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("Submitting"));
            _mockHtml.Document.Verify();
        }

        [Test]
        public void SubmitMyForm_Creates_A_Form_And_Submits_It()
        {
            ExpectCreateElement("form");
            ExpectCreateElement("input");
            _mockHtml.Document.Setup(doc => doc.Submit("MyForm")).Verifiable();

            _viewModel.ExecuteCommand(DocumentCommand("Submit('myForm')"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("Submitting..."));
            _mockHtml.Document.Verify();
        }

        [Test]
        public void DocumentReady_Sets_TestingDiv_Content()
        {
            _mockTestDiv.Setup(test => test.SetProperty("innerHTML", "Ready To Test")).Verifiable();
            
            _mockHtml.Document.Raise(doc => doc.DocumentReady += null, EventArgs.Empty);

            _mockTestDiv.Verify();
        }

        [Test]
        public void Window_Alert_Shows_Alert()
        {
            _mockHtml.Window.Setup(win => win.Alert("Alert")).Verifiable();

            _viewModel.ExecuteCommand(WindowCommand("Alert"));

            _mockHtml.Window.Verify();
        }

        [Test]
        public void Window_Confirm_Shows_Confirmation()
        {
            _mockHtml.Window.Setup(win => win.Confirm("Confirm")).Returns(true);

            _viewModel.ExecuteCommand(WindowCommand("Confirm"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("True"));
        }

        [Test]
        public void Window_Prompt_Shows_Prompt()
        {
            _mockHtml.Window.Setup(win => win.Prompt("Prompt")).Returns("Foo");

            _viewModel.ExecuteCommand(WindowCommand("Prompt"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("Foo"));
        }

        [Test]
        public void Window_CreateInstance_Creates_New_Window()
        {
            var newObject = new Mock<IScriptObject>();
            newObject.Setup(obj => obj.GetProperty("Name")).Returns("My name is Brian");
            _mockHtml.Window.Setup(win => win.CreateInstance("MyNameIs", new object[] {"Brian"}))
                .Returns(newObject.Object);

            _viewModel.ExecuteCommand(WindowCommand("CreateInstance"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("My name is Brian"));
        }

        [Test]
        public void Window_Navigate_To_Bookmark()
        {
            _mockHtml.Window.Setup(win => win.NavigateToBookmark("#BM")).Verifiable();

            _viewModel.ExecuteCommand(WindowCommand("NavigateToBookmark"));

            _mockHtml.Window.Verify();
        }

        [Test]
        public void Window_Current_Bookmark()
        {
            _mockHtml.Window.SetupGet(win => win.CurrentBookmark).Returns("#BM");

            _viewModel.ExecuteCommand(WindowCommand("CurrentBookmark"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("#BM"));
        }

        [Test]
        public void Window_Eval()
        {
            _mockHtml.Window.Setup(win => win.Eval("1 + 2")).Returns("3");

            _viewModel.ExecuteCommand(WindowCommand("Eval"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("3"));
        }

        [Test]
        public void Window_Navigate()
        {
            _mockHtml.Window.Setup(win => win.Navigate(new Uri("http://www.cnn.com/"))).Verifiable();

            _viewModel.ExecuteCommand(WindowCommand("Navigate"));

            _mockHtml.Window.Verify();
        }

        [Test]
        public void Window_Navigate_Target()
        {
            _mockHtml.Window.Setup(win => win.Navigate(new Uri("http://www.cnn.com/"), "_blank")).Verifiable();

            _viewModel.ExecuteCommand(WindowCommand("NavigateNew"));

            _mockHtml.Window.Verify();
        }

        [Test]
        public void Window_Navigate_Options()
        {
            _mockHtml.Window.Setup(win => win.Navigate(new Uri("http://www.cnn.com/"), "_blank", "height=200,width=400")).Verifiable();

            _viewModel.ExecuteCommand(WindowCommand("NavigateOptions"));

            _mockHtml.Window.Verify();
        }

        [Test]
        public void Element_AppendChild()
        {
            var mockImage = new Mock<IHtmlElement>();
            _mockHtml.Document.Setup(doc => doc.CreateElement("img")).Returns(mockImage.Object);
            _mockTestDiv.Setup(div => div.AppendChild(mockImage.Object)).Verifiable();

            _viewModel.ExecuteCommand(ElementCommand("AppendChild"));

            _mockTestDiv.Verify();
        }

        private static IEnumerable<Mock<IHtmlElement>> MockElements(int count)
        {
            for (int i = 0; i < count; i++)
                yield return new Mock<IHtmlElement>();
        }

        [Test]
        public void Element_AppendChildRelative()
        {
            var mockLIs = MockElements(4).ToList();
            var mockUL = new Mock<IHtmlElement>();
            var allElements = new Stack<Mock<IHtmlElement>>(mockLIs);
            allElements.Push(mockUL);

            _mockHtml.Document.Setup(doc => doc.CreateElement(It.IsAny<string>())).Returns(() => allElements.Pop().Object);
            mockUL.Setup(ul => ul.AppendChild(mockLIs[0].Object, mockLIs[2].Object)).Verifiable();

            _viewModel.ExecuteCommand(ElementCommand("AppendChildRelative"));

            mockUL.Verify();
        }

        [Test]
        public void Element_Children()
        {
            var mockChildren = new Mock<IScriptObjectCollection>();
            mockChildren.Setup(kids => kids.Count).Returns(3);
            _mockTestDiv.SetupGet(div => div.Children).Returns(mockChildren.Object);

            _viewModel.ExecuteCommand(ElementCommand("Children"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("TestDiv has 3 Children"));
        }

        [Test]
        public void Element_CssClass()
        {
            _mockTestDiv.SetupGet(div => div.CssClass).Returns("Woot");

            _viewModel.ExecuteCommand(ElementCommand("CssClass"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("Woot"));
        }

        [Test]
        public void Element_Focus()
        {
            var newInput = new Mock<IHtmlElement>();
            _mockHtml.Document.Setup(doc => doc.CreateElement("input")).Returns(newInput.Object);
            _mockTestDiv.Setup(div => div.AppendChild(newInput.Object));
            newInput.Setup(input => input.Focus()).Verifiable();

            _viewModel.ExecuteCommand(ElementCommand("Focus"));

            newInput.Verify();
        }

        [Test]
        public void Element_GetAttribute()
        {
            _mockTestDiv.Setup(div => div.GetAttribute("lang")).Returns("en");

            _viewModel.ExecuteCommand(ElementCommand("GetAttribute"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("en"));
        }

        [Test]
        public void Element_GetSetStyle()
        {
            _mockTestDiv.Setup(div => div.SetStyleAttribute("border", "thin dotted")).Verifiable();
            _mockTestDiv.Setup(div => div.GetStyleAttribute("border")).Returns("thin dotted");

            _viewModel.ExecuteCommand(ElementCommand("GetSetStyle"));

            _mockTestDiv.Verify();
            Assert.That(_viewModel.CommandResult, Is.EqualTo("thin dotted"));
        }

        [Test]
        public void Element_Id()
        {
            _mockTestDiv.Setup(div => div.Id).Returns("testing");

            _viewModel.ExecuteCommand(ElementCommand("Id"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("testing"));
        }

        [Test]
        public void Element_Parent()
        {
            var parent = new Mock<IHtmlElement>();
            parent.Setup(p => p.Id).Returns("MyParent");
            _mockTestDiv.Setup(div => div.Parent).Returns(parent.Object);

            _viewModel.ExecuteCommand(ElementCommand("Parent"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("MyParent"));
        }

        [Test]
        public void Element_RemoveAttribute()
        {
            _mockTestDiv.Setup(div => div.RemoveAttribute("lang")).Verifiable();
            _mockTestDiv.Setup(div => div.GetAttribute("lang")).Returns(string.Empty);
            
            _viewModel.ExecuteCommand(ElementCommand("RemoveAttribute"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("TestDiv Lang: "));
        }

        [Test]
        public void Element_RemoveChild()
        {
            var threeItems = MockElements(3).ToList();
            var ul = new Mock<IHtmlElement>();
            var allItems = new Stack<Mock<IHtmlElement>>(threeItems);
            allItems.Push(ul);
            _mockHtml.Document.Setup(doc => doc.CreateElement(It.IsAny<string>())).Returns(() => allItems.Pop().Object);
            ul.Setup(div => div.RemoveChild(threeItems[1].Object)).Verifiable();

            _viewModel.ExecuteCommand(ElementCommand("RemoveChild"));

            ul.Verify();
        }

        [Test]
        public void Element_RemoveStyle()
        {
            _mockTestDiv.Setup(div => div.RemoveStyleAttribute("border")).Verifiable();
            _mockTestDiv.Setup(div => div.GetStyleAttribute("border")).Returns(string.Empty).Verifiable();

            _viewModel.ExecuteCommand(ElementCommand("RemoveStyle"));

            _mockTestDiv.Verify();
            Assert.That(_viewModel.CommandResult, Is.EqualTo("Border Style: "));
        }

        [Test]
        public void Element_SetAttribute()
        {
            _mockTestDiv.Setup(div => div.SetAttribute("lang", "fr")).Verifiable();

            _viewModel.ExecuteCommand(ElementCommand("SetAttribute"));

            _mockTestDiv.Verify();
        }

        [Test]
        public void Element_TagName()
        {
            _viewModel.ExecuteCommand(ElementCommand("TagName"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("div"));
        }

        [Test]
        public void Element_AttachEvent1()
        {
            var newLink = new Mock<IHtmlElement>();
            _mockHtml.Document.Setup(doc => doc.CreateElement("a")).Returns(newLink.Object);

            EventHandler eventHandler = null;
            newLink.Setup(link => link.AttachEvent("mouseover", It.IsAny<EventHandler>()))
                .Callback((string name, EventHandler handler) => eventHandler = handler);

            _viewModel.ExecuteCommand(HtmlObjectCommand("AttachEvent1"));

            Assert.That(eventHandler, Is.Not.Null);
            eventHandler.Invoke(this, EventArgs.Empty);

            Assert.That(_viewModel.CommandResult.EndsWith(", MouseOver"));
        }

        [Test]
        public void Element_DetachEvent1()
        {
            var newLink = new Mock<IHtmlElement>();
            _mockHtml.Document.Setup(doc => doc.CreateElement("a")).Returns(newLink.Object);

            EventHandler eventHandler = null;
            newLink.Setup(link => link.AttachEvent("mouseover", It.IsAny<EventHandler>()))
                .Callback((string name, EventHandler handler) => eventHandler = handler);

            _viewModel.ExecuteCommand(HtmlObjectCommand("AttachEvent1"));

            newLink.Setup(link => link.DetachEvent("mouseover", eventHandler)).Verifiable();
            _mockHtml.Document.Setup(doc => doc.GetElementById("AttachEventLink1")).Returns(newLink.Object);

            _viewModel.ExecuteCommand(HtmlObjectCommand("DetachEvent1"));

            newLink.Verify();
        }

        private static IHtmlEventArgs HtmlEventArgs(int screenX, int screenY)
        {
            var mockArgs = new Mock<IHtmlEventArgs>();
            mockArgs.Setup(args => args.ScreenX).Returns(screenX);
            mockArgs.Setup(args => args.ScreenY).Returns(screenY);
            return mockArgs.Object;
        }

        [Test]
        public void Element_AttachEvent2()
        {
            var newLink = new Mock<IHtmlElement>();
            _mockHtml.Document.Setup(doc => doc.CreateElement("a")).Returns(newLink.Object);

            Action<object, IHtmlEventArgs> eventHandler = null;
            newLink.Setup(link => link.AttachEvent("mouseover", It.IsAny<Action<object, IHtmlEventArgs>>()))
                .Callback((string name, Action<object, IHtmlEventArgs> handler) => eventHandler = handler);

            _viewModel.ExecuteCommand(HtmlObjectCommand("AttachEvent2"));

            Assert.That(eventHandler, Is.Not.Null);
            eventHandler(this, HtmlEventArgs(50, 60));

            Assert.That(_viewModel.CommandResult.EndsWith(", MouseOver (50, 60)"));
        }

        [Test]
        public void Element_DetachEvent2()
        {
            var newLink = new Mock<IHtmlElement>();
            _mockHtml.Document.Setup(doc => doc.CreateElement("a")).Returns(newLink.Object);

            Action<object, IHtmlEventArgs> eventHandler = null;
            newLink.Setup(link => link.AttachEvent("mouseover", It.IsAny<Action<object, IHtmlEventArgs>>()))
                .Callback((string name, Action<object, IHtmlEventArgs> handler) => eventHandler = handler);

            _viewModel.ExecuteCommand(HtmlObjectCommand("AttachEvent2"));

            newLink.Setup(link => link.DetachEvent("mouseover", eventHandler)).Verifiable();
            _mockHtml.Document.Setup(doc => doc.GetElementById("AttachEventLink2")).Returns(newLink.Object);

            _viewModel.ExecuteCommand(HtmlObjectCommand("DetachEvent2"));

            newLink.Verify();
        }

        [Test]
        public void ScriptObject_CheckAccess()
        {
            _mockTestDiv.Setup(div => div.CheckAccess()).Returns(true);

            _viewModel.ExecuteCommand(ScriptObjectCommand("CheckAccess"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("True"));
        }

        [Test]
        public void ScriptObject_ConvertTo()
        {
            var scriptObject = new Mock<IScriptObject>();
            _mockHtml.Window.Setup(win => win.Invoke("GetList", 5)).Returns(scriptObject.Object);
            scriptObject.Setup(so => so.ConvertTo<string[]>()).Returns(new[] {"a", "b", "c"});

            _viewModel.ExecuteCommand(ScriptObjectCommand("ConvertTo"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("a, b, c"));
        }

        [Test]
        public void ScriptObject_GetPropertyIndexed()
        {
            var scriptObject = new Mock<IScriptObject>();
            scriptObject.Setup(so => so.GetProperty(2)).Returns("Foo");
            _mockHtml.Window.Setup(win => win.Invoke("GetList", 5)).Returns(scriptObject.Object);

            _viewModel.ExecuteCommand(ScriptObjectCommand("GetProperty_Indexed"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("Foo"));
        }

        [Test]
        public void ScriptObject_GetPropertyNamed()
        {
            var scriptObject = new Mock<IScriptObject>();
            scriptObject.Setup(so => so.GetProperty("Name")).Returns("My Name is Brian");
            _mockHtml.Window.Setup(win => win.Invoke("CreateName", "Brian")).Returns(scriptObject.Object);

            _viewModel.ExecuteCommand(ScriptObjectCommand("GetProperty_Named"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("My Name is Brian"));
        }

        [Test]
        public void ScriptObject_InvokeSelf()
        {
            var funcObject = new Mock<IScriptObject>();
            _mockHtml.Window.Setup(win => win.GetProperty("GetList")).Returns(funcObject.Object);
            var arrayObject = new Mock<IScriptObject>();
            funcObject.Setup(func => func.InvokeSelf(5)).Returns(arrayObject.Object);
            arrayObject.Setup(a => a.GetProperty(2)).Returns("FooBar");

            _viewModel.ExecuteCommand(ScriptObjectCommand("InvokeSelf"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("FooBar"));
        }

        [Test]
        public void ScriptObject_ManagedObject()
        {
            var scriptObject = new Mock<IScriptObject>();
            _mockHtml.Window.Setup(win => win.Invoke("GetScriptableObject")).Returns(scriptObject.Object);
            scriptObject.SetupGet(so => so.ManagedObject).Returns(this);

            _viewModel.ExecuteCommand(ScriptObjectCommand("ManagedObject"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("ManagedObject is: HtmlUtilities.UnitTests.MainPageViewModelTests"));
        }

        [Test]
        public void ScriptObject_SetPropertyIndexed()
        {
            var scriptObject = new Mock<IScriptObject>();
            _mockHtml.Window.Setup(win => win.Invoke("GetList", 5)).Returns(scriptObject.Object);
            scriptObject.Setup(so => so.SetProperty(2, "Modified Value")).Verifiable();
            scriptObject.Setup(so => so.ConvertTo<string[]>()).Returns(new [] {"a", "b", "Modified Value", "d", "e"});

            _viewModel.ExecuteCommand(ScriptObjectCommand("SetProperty_Indexed"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("a, b, Modified Value, d, e"));
            scriptObject.Verify();
        }

        [Test]
        public void ScriptObject_SetPropertyNamed()
        {
            var scriptObject = new Mock<IScriptObject>();
            _mockHtml.Window.Setup(win => win.Invoke("CreateName", "Brian")).Returns(scriptObject.Object);
            scriptObject.Setup(so => so.SetProperty("Name", "Cara")).Verifiable();
            scriptObject.Setup(so => so.GetProperty("Name")).Returns("Cara");

            _viewModel.ExecuteCommand(ScriptObjectCommand("SetProperty_Named"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("Cara"));
            scriptObject.Verify();
        }

        [Test]
        public void Page_IsEnabled()
        {
            _mockHtml.Page.Setup(page => page.IsEnabled).Returns(true);

            _viewModel.ExecuteCommand(PageCommand("IsEnabled"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("True"));
        }

        [Test]
        public void Page_IsPopupWindowAllowed()
        {
            _mockHtml.Page.Setup(page => page.IsPopupWindowAllowed).Returns(true);

            _viewModel.ExecuteCommand(PageCommand("IsPopupWindowAllowed"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("True"));
        }

        [Test]
        public void Page_Plugin()
        {
            var plugin = new Mock<IHtmlElement>();
            _mockHtml.Page.Setup(page => page.Plugin).Returns(plugin.Object);
            plugin.Setup(p => p.Id).Returns("SLPlugin");

            _viewModel.ExecuteCommand(PageCommand("Plugin"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("SLPlugin"));
        }

        [Test]
        public void Page_PopupWindow()
        {
            _mockHtml.Page.Setup(page => page.PopupWindow(new Uri("http://www.cnn.com"), "_blank",
                                                          It.IsAny<HtmlPopupWindowOptions>()))
                                                          .Verifiable();

            _viewModel.ExecuteCommand(PageCommand("PopupWindow"));

            _mockHtml.Page.Verify();
        }

        [Test]
        public void Page_RegisterCreateableType()
        {
            _mockHtml.Page.Setup(page => page.RegisterCreateableType("SLName", typeof( PageCommands.Name))).Verifiable();
            var scriptObject = new Mock<IScriptObject>();
            _mockHtml.Window.Setup(win => win.Invoke("CreateSLName", "Brian", "Genisio")).Returns(scriptObject.Object);
            scriptObject.Setup(so => so.ManagedObject).Returns(new PageCommands.Name {First = "Maia", Last = "Genisio"});

            _viewModel.ExecuteCommand(PageCommand("RegisterCreatableType"));

            _mockHtml.Page.Verify();
            Assert.That(_viewModel.CommandResult, Is.EqualTo("Maia Genisio"));
        }

        [Test]
        public void Page_UnregisterCreateableType()
        {
            _mockHtml.Page.Setup(page => page.UnregisterCreateableType("SLName")).Verifiable();
            _mockHtml.Window.Setup(win => win.Invoke("CreateSLName", "Brian", "Genisio")).Throws(new Exception());

            _viewModel.ExecuteCommand(PageCommand("UnregisterCreateableType"));
            
            _mockHtml.Page.Verify();

            Assert.That(_viewModel.CommandResult, Is.EqualTo("Succesfully unregistered"));
        }

        [Test]
        public void SOCollection_Count()
        {
            var collection = new Mock<IScriptObjectCollection>();
            _mockTestDiv.Setup(div => div.Children).Returns(collection.Object);
            collection.Setup(coll => coll.Count).Returns(5);

            _viewModel.ExecuteCommand(SOCOllectionCommand("Count"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("5"));
        }

        [Test]
        public void SOCOllection_Indexer()
        {
            var item1 = new Mock<IHtmlElement>();
            item1.Setup(item => item.TagName).Returns("Foo");
            var item2 = new Mock<IHtmlElement>();
            item2.Setup(item => item.TagName).Returns("Bar");

            var collection = new Mock<IScriptObjectCollection>();
            collection.Setup(coll => coll.Count).Returns(2);
            collection.Setup(coll => coll[0]).Returns(item1.Object);
            collection.Setup(coll => coll[1]).Returns(item2.Object);

            _mockTestDiv.Setup(div => div.Children).Returns(collection.Object);

            _viewModel.ExecuteCommand(SOCOllectionCommand("Indexer"));

            Assert.That(_viewModel.CommandResult, Is.EqualTo("Foo, Bar"));
        }
    }
}
